Skip to content

fix: roundTo returns null for a null value instead of throwing - #9

Merged
marc0olo merged 1 commit into
mainfrom
fix/roundto-null-safe
Aug 23, 2026
Merged

fix: roundTo returns null for a null value instead of throwing#9
marc0olo merged 1 commit into
mainfrom
fix/roundto-null-safe

Conversation

@marc0olo

Copy link
Copy Markdown
Member

Live 500 on the NFT detail page. https://soon.market/nft/4398046952294 and .../4398046704949 both show "Something went wrong":

java.lang.NullPointerException
  at com.kryptokrauts.shared.BaseMapper.roundTo(BaseMapper.java:124)
  at com.kryptokrauts.shared.BaseMapper.roundTo(BaseMapper.java:128)
  at com.kryptokrauts.soonmarket.service.NFTService.getNFTDetail(NFTService.java:662)

Cause

roundTo unboxes its argument:

return Math.round(value * Math.pow(10, decimals)) / Double.valueOf(Math.pow(10, decimals));

That was harmless while every usd value was a number. #7 made them null when the token has no exchange rate, and every caller that hands a usd value straight to roundTo began throwing instead of rendering nothing.

NFTService:662 is one:

.priceUSD(BaseMapper.roundTo(floorListing.getListingPriceUsd()))

So the detail page 500s for any NFT whose template has a floor listing priced in FOOBAR, RDM or EASY. That is why it looks scattered rather than affecting a whole category — it depends on the template's floor listing, not on the NFT itself.

Fix

Rounding an unknown value yields an unknown value. The guard belongs in roundTo, not at each caller: there are 22 callers across 8 files in soon-market-api alone, plus more in the other consumers, and every one of them wants the same answer.

What went wrong in #7

The audit behind #7 covered buildPriceInfo and stopped there. Making a value nullable is only safe once everything that consumes it can take a null, and roundTo is the most widely shared of those consumers. Callers outside the method I changed were never checked.

Consumers

Needs the usual submodule bump. soon-market-api is the urgent one — it is where the 500 is. event-processor-contract should follow for consistency.

Verification

soon-market-api compiles against this source. The change cannot break an existing caller: anything passing a non-null value behaves exactly as before, and anything passing null was throwing.

🤖 Generated with Claude Code

#7 made usd values null when the token has no exchange rate. roundTo
unboxes its argument, so every caller that hands it a usd value directly
started throwing a NullPointerException rather than rendering nothing:

    java.lang.NullPointerException
      at BaseMapper.roundTo(BaseMapper.java:124)
      at BaseMapper.roundTo(BaseMapper.java:128)
      at NFTService.getNFTDetail(NFTService.java:662)

That is a live 500 on the nft detail page, for any nft whose template
has a floor listing priced in FOOBAR, RDM or EASY - the page shows
"Something went wrong".

Rounding an unknown value yields an unknown value, so the guard belongs
here rather than at each caller: there are more than twenty of them
across several repositories and every one wants the same answer.

The audit behind #7 covered buildPriceInfo and stopped there. It should
have covered every caller of roundTo, since making a value nullable is
only safe once everything that consumes it can take a null.

Refs #7

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@marc0olo
marc0olo merged commit 916b736 into main Aug 23, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant